package cn.piesat.scanning.business.nongqi.single;

import cn.hutool.core.date.DateUtil;
import cn.piesat.scanning.business.common.algorithm.processAlgorithm.SingleTaskAbstract;
import cn.piesat.scanning.business.nongqi.vo.RuleVO;
import cn.piesat.scanning.dto.*;
import cn.piesat.scanning.service.*;
import cn.piesat.scanning.utils.FileUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FilenameFilter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author xhl
 */
@Component
public class NqSingleTask extends SingleTaskAbstract {
    private Logger log = LoggerFactory.getLogger(NqSingleTask.class);

    @Value("${scanning.data.day}")
    private Integer nearDay;

    private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private static final String NULL = "NULL";
    /**
     * 同一谱段不同通道的时间差
     */
    private static final int DIFF_TIME_MINUTE = 3;

    private static final Logger logger = LoggerFactory.getLogger(NqSingleTask.class);

    @Autowired
    private HtScanningMetadataService htScanningMetadataService;
    @Autowired
    private DbDmsSchedulerTaskFileRecordService dbDmsSchedulerTaskFileRecordService;
    @Autowired
    private HtPathConfigurationService htPathConfigurationService;
    @Autowired
    private DbDmsScanningMetadataAuxInfoService dbDmsScanningMetadataAuxInfoService;
    @Autowired
    private DbDmsScanningValidationService dbDmsScanningValidationService;
    @Autowired
    private HtAlgorithmConfigurationService htAlgorithmConfigurationService;

    @Override
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams,
                                     Map<String, Integer> indexParams, HtScheduleJobDTO taskDTO) {
        //规则参数
        AlgorithmTaskRuleParam taskRuleParam = JSON.parseObject(JSON.toJSONString(ruleParams), AlgorithmTaskRuleParam.class);
        //路径参数
        AlgorithmTaskPathParam taskPathParam = JSON.parseObject(JSON.toJSONString(inputParams), AlgorithmTaskPathParam.class);
        //判断规则不为NULL但是输入路径为空的情况
        if (!rulePathExist(taskRuleParam, taskPathParam)) {
            return;
        }
        Boolean preProcess = false;

        JSON.parseObject(taskRuleParam.getPrimaryFile(), RuleVO.class);
        RuleVO primaryRuleVO = JSON.parseObject(taskRuleParam.getPrimaryFile(), RuleVO.class);
        //查找可执行的数据，默认查找配置文件中配置的时间文件，[状态为重做的文件]，记录表中不存在的主文件
        List<HtScanningMetadataDTO> execMetadataList = new ArrayList<>();
        HtAlgorithmConfigurationDTO algorithmDTO = htAlgorithmConfigurationService.findById(taskDTO.getAlgoId());
        //获取算法标识 SWAPDSTDET
        String adsType = algorithmDTO.getAlgorithmIdentify();
        execMetadataList.addAll(selectExecMainFileList(primaryRuleVO, preProcess, taskDTO, adsType));
        execMetadataList.addAll(findRedoMetadataList(taskDTO.getId()));
        logger.info("集合数量:" + String.valueOf(execMetadataList.size()));
        assemble(taskRuleParam, taskPathParam, indexParams, execMetadataList, taskDTO);

    }

    @Override
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams, HtScheduleJobDTO taskDTO) {

    }


    private void assemble(AlgorithmTaskRuleParam taskRuleParam, AlgorithmTaskPathParam taskPathParam, Map<String, Integer> indexParams,
                          List<HtScanningMetadataDTO> execMetadataList, HtScheduleJobDTO taskDTO) {
        //获取数据路径
        HtPathConfigurationDTO pathDTO = htPathConfigurationService.findById(taskDTO.getPathConfigId());
        Boolean preProcess = false;
        //获取算法信息 （算法名称，算法标识等）
        HtAlgorithmConfigurationDTO algorithmDTO = htAlgorithmConfigurationService.findById(taskDTO.getAlgoId());
        //获取算法标识 SWAPDSTDET
        String adsType = algorithmDTO.getAlgorithmIdentify();
        for (HtScanningMetadataDTO metadataDTO : execMetadataList) {
            Map<String, Object> execParams = new HashMap<>();
            //匹配primaryFile规则
            String primaryFile = pathDTO.getScanningPath() + File.separator + metadataDTO.getFilePath();
            execParams.put("preprocess", preProcess);
            execParams.put("algorithmId", taskDTO.getAlgoId());
            execParams.put("pathConfigId", taskDTO.getPathConfigId());
            execParams.put("fileDate", metadataDTO.getFileDate());
            execParams.put("lon", metadataDTO.getLon());
            execParams.put("lat", metadataDTO.getLat());
            execParams.put("taskPlanId", metadataDTO.getTaskPlanId());
            execParams.put("subPlanId", metadataDTO.getSubPlanId());
            execParams.put("viewNumber", metadataDTO.getViewNumber());
            execParams.put("productDir", pathDTO.getOutputPath());
            execParams.put("productType", taskPathParam.getProductType());
            //组装算法参数
            Map<String, Object> algorithmParams = new HashMap<>();
            try {
                algorithmParams = initAlgorithmParams(adsType, primaryFile, metadataDTO.getFilePath(), taskRuleParam, taskPathParam, indexParams, metadataDTO.getFileDate());
            } catch (ParseException e) {
                e.printStackTrace();
            }
            if ((algorithmParams.get("parameter01") == null && adsType.equals("SWAPLIGHT"))
            ) {
                log.info("数据不存在,已经跳过");
                continue;
            } else {
                log.info("数据存在");
                updateRecordAndSendMessage(execParams, taskDTO, metadataDTO.getFileName(), metadataDTO.getFileDate(), algorithmParams);
            }
        }
    }

    private Map<String, Object> initAlgorithmParams(String adsType, String primaryFile, String filePath, AlgorithmTaskRuleParam taskRuleParam,
                                                    AlgorithmTaskPathParam taskPathParam, Map<String, Integer> indexParams, String fileDate) throws ParseException {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
        primaryFile = FilenameUtils.separatorsToUnix(primaryFile);
        Map<String, Object> algorithmParams = new HashMap<>();
        algorithmParams.put("parameter00", primaryFile);
//        algorithmParams.put("parameter01", checkVal(taskPathParam.getParameter01()));
//        algorithmParams.put("parameter02", checkVal(taskPathParam.getParameter02()));
//        algorithmParams.put("parameter03", checkVal(taskPathParam.getParameter03()));
//        algorithmParams.put("parameter04", checkVal(taskPathParam.getParameter04()));
//        algorithmParams.put("parameter05", checkVal(taskPathParam.getParameter05()));
//        algorithmParams.put("parameter06", checkVal(taskPathParam.getParameter06()));
//        algorithmParams.put("parameter07", checkVal(taskPathParam.getParameter07()));
//        algorithmParams.put("parameter08", checkVal(taskPathParam.getParameter08()));
//        algorithmParams.put("parameter09", checkVal(taskPathParam.getParameter09()));
        if ("fy3d_east_west_mosaic".equals(adsType)) {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("westFile", primaryFile.replace("EAST", "WEST"));
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("FY3_MOD_NDVI00_CROP_STATIC".equals(adsType)) {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("productName", taskPathParam.getParameter01());
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("fy3d_crop_product".equals(adsType)) {
            // /share/download/FY3/FY3D_NDVI/R10DAY/1000M/FY3D_MERSI_REGN_L3_NVI_MLT_GLL_20221023_ROTD_1000M_MS.HDF
            String oldYear = fileDate.substring(0, 8);
            LocalDate parse = LocalDate.parse(oldYear, dtf);
//             LocalDate lastYearDate = parse.minusYears(1);
            Map<String, String> resultMap = new HashMap<>();
            Map<String, String> l3resultMap = new HashMap<>();
            getFileMap(parse, resultMap, l3resultMap);
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", resultMap);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("l3File", l3resultMap);
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("FY3D_NDVI01_CHN_ROTD".equals(adsType) || "FY3D_NDVI01_POTD".equals(adsType)) {
            // /share/download/FY3/FY3D_NDVI/R10DAY/1000M/FY3D_MERSI_REGN_L3_NVI_MLT_GLL_20221023_ROTD_1000M_MS.HDF
            String oldYear = fileDate.substring(0, 4);
            Integer i = Integer.parseInt(oldYear) - 1;
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("L3File", primaryFile.replaceAll(oldYear, i.toString())); //获取L3文件上一年的数据
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("FY3D_VSWI_POTD".equals(adsType)) {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("lstFile", taskPathParam.getParameter01() + filePath.replaceAll("NVI", "LST"));
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("FY3D_LST_CHN_ROTD".equals(adsType) || "FY3D_LST_CHN_POAD".equals(adsType)) {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("copyPath", taskPathParam.getParameter01());
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else if ("FY3D_MVSWI_POTD".equals(adsType)) {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFileNDVI", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            params.put("primaryFileLST", taskPathParam.getParameter01() + filePath.replaceAll("NVI", "LST"));
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        } else {
            Map<String, Object> params = new HashMap<>();
            params.put("primaryFile", primaryFile);
            params.put("auxPath", taskPathParam.getParameter02());
            algorithmParams.put("parameter00", adsType);
            algorithmParams.put("parameter01", params);
        }

        return algorithmParams;
    }

    private String checkVal(String value) {
        if (!StringUtils.isEmpty(value)) {
            return value;
        }
        return "";
    }


    /**
     * 获取要执行的文件
     *
     * @param sourceDate
     * @return
     */
    private boolean getFileMap(LocalDate sourceDate, Map<String, String> resultMap, Map<String, String> l3resultMap) {
        String primaryYear = String.valueOf(sourceDate.getYear());
        String l3Year = String.valueOf(sourceDate.minusYears(1).getYear());
        String filePath = FileUtil.getInstance().getDataFormatePath("/share/product/FY3D/NDVI00/|yyyy|/|yyyy|POAY", sourceDate);
        File sncFile = new File(filePath);
        if (!sncFile.exists()) {
            logger.error("执行算法失败，源文件不存在。{}", filePath);
            return false;
        }
        //获取文件
        File[] files = sncFile.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                System.out.println(name);
                if (name.contains(".TIFF")) {
                    return true;
                }
                return false;
            }
        });
        if (files.length == 0) {
            logger.error("执行算法失败，目录{}下没有可执行的文件。", filePath);
            return false;
        }
        //解析文件，分组返回
        for (File file : files) {
            //FY3D_MERSI_GLL_NDVI00_2021_POAY_ARG--_MAIZE_1000M.TIFF
            ///FY3D_MERSI_GLL_NDVI00_2021_POAY_AUS--_WHEAT_1000M.TIFF
            //   0    1     2    3     4     5   6     7      8
            String[] namestr = file.getName().split("_");
            //按区域和作物类型分组
            String key = namestr[6].replace("--", "") + "_" + namestr[7];
//            System.out.println("测试=====primaryFile：" + key + ":" + file.getPath());
            resultMap.put(key, file.getPath());
            //获得L3文件
//            System.out.println("测试=====l3File：" + key + ":" + file.getPath().replace(primaryYear, l3Year));
            l3resultMap.put(key, file.getPath().replace(primaryYear, l3Year));
        }
        return true;
    }

    //如果是开始时间
    private static List<String[]> getFilesByTime(String filePath, String startTime, String endTime, String matche) {

        List<String[]> filePaths = new ArrayList<String[]>();
        List<String[]> truefilePaths = new ArrayList<String[]>();
        if (startTime.substring(0, 8).equals(endTime.substring(0, 8))) {
            //同一个文件夹的处理
            ///mnt/FY4DATA/AGRI/L1/FDI/DISK\\2021\\20210909
            String startFilePath = filePath + File.separator + startTime.substring(0, 4) + File.separator + startTime.substring(0, 8);
            filePaths.addAll(getFiles(startFilePath, matche));
        } else {
            String startFilePath = filePath + File.separator + startTime.substring(0, 4) + File.separator + startTime.substring(0, 8);
            String endFilePath = filePath + File.separator + endTime.substring(0, 4) + File.separator + endTime.substring(0, 8);
            filePaths.addAll(getFiles(startFilePath, matche));
            filePaths.addAll(getFiles(endFilePath, matche));
        }
        //比较时间是否在访问内
        for (String[] time : filePaths) {
            if (Long.valueOf(time[0]) >= Long.valueOf(startTime) && Long.valueOf(time[0]) < Long.valueOf(endTime)) {
                // filePaths.remove(time);
                truefilePaths.add(time);
            }
        }
        return truefilePaths;

    }

    //返回 时次  文件名 数组
    private static List<String[]> getFiles(String startFilePath, String matche) {
        List<String[]> filePaths = new ArrayList<String[]>();
        File startFileDir = new File(startFilePath);
        File[] startFiles = startFileDir.listFiles();
        if (startFiles == null) return filePaths;
        for (File file : startFiles) {
            //先匹配到文件
            String fn = FileUtil.getFileName(file.getAbsolutePath());
            //if(fn.matches(".*FDI.*4000M_V0001.HDF$")) { --matche
            if (fn.matches(matche)) {
                //匹配到文件
                String[] nameTime = new String[2];
                Pattern p = Pattern.compile("(\\d{4})(\\d{1,2})(\\d{1,2})(\\d{1,2})(\\d{1,2})(\\d{1,2})");
                Matcher m = p.matcher(fn);
                String time = "";
                while (m.find()) {
                    time = m.group();
                    break;
                }
                nameTime[0] = time;
                nameTime[1] = fn;
                filePaths.add(nameTime);
            }
        }
        return filePaths;
    }

    private void getclp(Map<String, Object> callParamterMap, String parameter, String time) {
        // //时间格式为 20190304230000
        String clp = (String) callParamterMap.get(parameter);
        //获取年月日

        String year = time.substring(0, 4);
        String moth = time.substring(0, 8);
        //获得L2级路径
        String l2FilePath = clp + "/" + year + "/" + moth;
        String filepath = "";
        File f = new File(l2FilePath);
        File[] fs = f.listFiles();
        for (File ff : fs) {
            String fn = FileUtil.getFileName(ff.getAbsolutePath());
            if (fn.contains(time) && fn.endsWith("4000M_V0001.NC")) {

                filepath = clp + "\\" + year + "\\" + moth + "\\" + fn;

                callParamterMap.put(parameter, filepath.replace("\\", "/"));
                break;
            }
        }

    }

    //检验源文件规则解析
    private String validationRule(String validationFile, String fileDate, String validationFile1) {
        StringBuilder filePathSb = new StringBuilder();
        if (StringUtils.isBlank(validationFile)) {
            return filePathSb.toString();
        }
        RuleVO ruleVO = JSON.parseObject(validationFile, RuleVO.class);
        if (NULL.equals(ruleVO.getMatchRule()) || NULL.equals(ruleVO.getLevel())) {
            return filePathSb.toString();
        }
        Map<String, String> timeRangeParam = parseTimeRange(fileDate, ruleVO.getTimeRange());
        String mathexp = ruleVO.getMatchRule();
        String satellite = ruleVO.getSatellite();
        String startTime = timeRangeParam.get("startTime");
        String endTime = timeRangeParam.get("endTime");
        List<DbDmsScanningValidationDTO> ruleFileList = dbDmsScanningValidationService
                .findBySatelliteAndDataLevelAndProductIdentifyAndFileDateBetween(satellite, ruleVO.getLevel(), mathexp, startTime, endTime);
        if (ruleFileList.isEmpty()) {
            logger.info("未查询到任何质检源数据文件");
            return null;
        }
        for (DbDmsScanningValidationDTO fileDTO : ruleFileList) {
            String currentFilePath = validationFile1 + File.separator + fileDTO.getFilePath();
            File file = new File(currentFilePath);
            if (!file.exists()) {
                logger.info("{}在磁盘上不存在", fileDTO.getFileName());
                return null;
            }
            filePathSb.append(",").append(currentFilePath);
        }

        return filePathSb.substring(1);
    }

    /**
     * @param inputParams 输入参数
     * @param ruleParams  规则参数
     * @param taskDTO     任务信息对象
     * @param data        处理的数据对象，自己定义
     */
    @Override
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams,
                                     HtScheduleJobDTO taskDTO, Object data) {
        HtScanningMetadataDTO metadataDTO = (HtScanningMetadataDTO) data;
        List<HtScanningMetadataDTO> dataList = new ArrayList<>();
        dataList.add(metadataDTO);
        AlgorithmTaskRuleParam taskRuleParam = JSON.parseObject(JSON.toJSONString(ruleParams), AlgorithmTaskRuleParam.class);
        AlgorithmTaskPathParam taskPathParam = JSON.parseObject(JSON.toJSONString(inputParams), AlgorithmTaskPathParam.class);
        assemble(taskRuleParam, taskPathParam, dataList, taskDTO);
    }

    private void assemble(AlgorithmTaskRuleParam taskRuleParam, AlgorithmTaskPathParam taskPathParam,
                          List<HtScanningMetadataDTO> dataList, HtScheduleJobDTO taskDTO) {
        assemble(taskRuleParam, taskPathParam, null, dataList, taskDTO);
    }

    /**
     * 校验输入规则的文件是否写入路径
     *
     * @param ruleParam
     * @param pathParam
     * @return
     */
    private boolean rulePathExist(AlgorithmTaskRuleParam ruleParam, AlgorithmTaskPathParam pathParam) {
        if (StringUtils.isBlank(ruleParam.getPrimaryFile())) {
            logger.info("主文件规则为空");
            return false;
        }
        /*if (StringUtils.isNotBlank(ruleParam.getGeoFile())) {
            RuleVO geoRuleVO = JSON.parseObject(ruleParam.getGeoFile(), RuleVO.class);
            if (!NULL.equals(geoRuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getGeoFile())) {
                logger.info("geo文件输入路径为空");
                return false;
            }
        }
        if (StringUtils.isNotBlank(ruleParam.getL2File())) {
            RuleVO l2RuleVO = JSON.parseObject(ruleParam.getL2File(), RuleVO.class);
            if (!NULL.equals(l2RuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getL2File())) {
                logger.info("L2文件输入路径为空");
                return false;
            }
        }
        if (StringUtils.isNotBlank(ruleParam.getNwpFile())) {
            RuleVO nwpRuleVO = JSON.parseObject(ruleParam.getNwpFile(), RuleVO.class);
            if (!NULL.equals(nwpRuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getNwpFile())) {
                logger.info("辅助（nwp）文件输入路径为空");
                return false;
            }
        }*/
        return true;
    }


    protected List<HtScanningMetadataDTO> selectExecMainFileList(RuleVO primaryRuleVO, boolean preprocess,
                                                                 HtScheduleJobDTO taskDTO, String adsType) {
        //获取文件匹配规则
        if (StringUtils.isBlank(primaryRuleVO.getMatchRule()) || NULL.equals(primaryRuleVO.getMatchRule())) {
            new ArrayList<>();
        }
        String mainMathexp = primaryRuleVO.getMatchRule().split(",|\\|")[0];
        //产品标识
        String productIdentify = primaryRuleVO.getProductIdentify();
        //任务类型
        String taskType = taskDTO.getTaskType();
        LocalDateTime now = LocalDateTime.now();
        //从算法执行时间往前推一百天
        Date redoStartTime = taskDTO.getRedoStartTime();
        Calendar cal = Calendar.getInstance();
        cal.setTime(redoStartTime);
        String startTime = TIME_FORMAT.format(cal.toInstant().atZone(ZoneId.systemDefault()));
//        String startTime = TIME_FORMAT.format(now.plusDays(-1L * nearDay));
        String endTime = TIME_FORMAT.format(now);
        Pattern p = Pattern.compile(mainMathexp);
        if ("redo_singleTask".equals(taskType)) {
//            Date redoStartTime = taskDTO.getRedoStartTime();
            Date redoEndTime = taskDTO.getRedoEndTime();

            Calendar cale = Calendar.getInstance();
            cale.setTime(redoEndTime);
//            startTime = TIME_FORMAT.format(redoStartTime.toInstant().atZone(ZoneId.systemDefault()));
            endTime = TIME_FORMAT.format(cale.toInstant().atZone(ZoneId.systemDefault()));
        }
        List<HtScanningMetadataDTO> metaDataList = null;
        List<HtScanningMetadataDTO> allMetaDataList = new ArrayList<>();
        /*
            预处理：根据传入的传感器，时间范围，数据等级查询。
            专业处理：根据传入的通道，时间范围，文件类型（L2X、L2Y的文件类型为TIFF），数据等级查询。
         */
        /*if (preprocess) {
            allMetaDataList = htScanningMetadataService.listBySensorAndRangeTimeAndLevel(mainMathexp,startTime,endTime,primaryRuleVO.getLevel());
        } else {
            allMetaDataList = htScanningMetadataService.listByBandsAndDataLevelAndProductIidAndRangeTime(mainMathexp,primaryRuleVO.getLevel(),productIdentify,startTime,endTime);
        }*/

        metaDataList = htScanningMetadataService.findByMetadata(startTime, endTime, primaryRuleVO.getLevel(), primaryRuleVO.getSatellite(), primaryRuleVO.getProductIdentify());
        for (HtScanningMetadataDTO dto : metaDataList) {
            Matcher matcher = p.matcher(dto.getFileName());
            if (matcher.matches()) {
                allMetaDataList.add(dto);
            }
        }
        List<String> allNameList = allMetaDataList.stream().map(x -> x.getFileName()).collect(Collectors.toList());

        List<DbDmsSchedulerTaskFileRecordDTO> existRecordDataList = dbDmsSchedulerTaskFileRecordService.findByTaskIdAndFileNameIn(taskDTO.getId(), allNameList);
        List<HtScanningMetadataDTO> execMetadataList = null;
        //过滤需要执行的主文件，如果记录表中为空则查询出的时间范围内所有文件都执行，如果不为空，则筛选掉已经执行过的。
        if (existRecordDataList.isEmpty()) {
            execMetadataList = allMetaDataList;
        } else {
            Map<String, DbDmsSchedulerTaskFileRecordDTO> hash = existRecordDataList.stream().
                    collect(Collectors.toMap(DbDmsSchedulerTaskFileRecordDTO::getFileName, dto -> dto, (v1, v2) -> v2));
            execMetadataList = allMetaDataList.stream().filter(dto -> !hash.containsKey(dto.getFileName())).collect(Collectors.toList());
        }
        if ("fy3d_crop_product".equals(adsType)) {
            List<HtScanningMetadataDTO> oneList = new ArrayList<>();
            if (execMetadataList.size() == 0) {
                return new ArrayList<>();
            }
            oneList.add(execMetadataList.get(0));
            return oneList;
        } else {
            return execMetadataList;
        }

    }

    protected List<HtScanningMetadataDTO> findRedoMetadataList(String taskId) {
        List<DbDmsSchedulerTaskFileRecordDTO> redoTaskRecordList = dbDmsSchedulerTaskFileRecordService.findRedoTasks(taskId);
        List<String> fileNameList = redoTaskRecordList.stream()
                .map(DbDmsSchedulerTaskFileRecordDTO::getFileName).collect(Collectors.toList());
        return htScanningMetadataService.findByFileNameList(fileNameList);
    }

    private String primaryRule(RuleVO primaryFileRule, boolean preprocess, HtScanningMetadataDTO mainFileDTO,
                               HtPathConfigurationDTO pathDTO, String matchRule) {
        //主文件规则时间范围
        String mainFileDate = mainFileDTO.getFileDate();
        Map<String, String> timeRangeParam = parseTimeRange(mainFileDate, primaryFileRule.getTimeRange());
        //查找满足要求的所有文件列表
        String[] mathexpArr = matchRule.split("\\|");
        String leftTime = timeRangeParam.get("startTime");
        String rightTime = timeRangeParam.get("endTime");
        //如果没有输入时间参数，则默认考虑正负3分钟查找文件，经纬度满足正负0.5°
        if (leftTime.equals(rightTime)) {
            return findPrimaryFileList(mainFileDTO, primaryFileRule.getLevel(), pathDTO, Arrays.asList(mathexpArr), preprocess);
        } else {
            //1、根据主通道找到输入的时间范围内的所有主通道文件。2、根据不同时次的主通道正负3分钟找到同一谱段的其他通道文件，拼接
            List<HtScanningMetadataDTO> mainBandsFileList = htScanningMetadataService
                    .listByBandsAndDataLevelAndRangeTime(mainFileDTO.getBands(), primaryFileRule.getLevel(), leftTime, rightTime);
            if (mainBandsFileList.isEmpty()) {
                logger.info("未查询到多个谱段的主通道文件时");
                return null;
            }
            StringBuilder primaryFilePath = new StringBuilder();
            for (HtScanningMetadataDTO dto : mainBandsFileList) {
                String path = findPrimaryFileList(dto, primaryFileRule.getLevel(), pathDTO, Arrays.asList(mathexpArr), preprocess);
                if (path == null) {
                    return null;
                }
                primaryFilePath.append(";").append(path);
            }
            return primaryFilePath.substring(1);
        }
    }

    /**
     * 获取primaryFile文件列表信息
     *
     * @param mainFileDTO
     * @param level
     * @param bandsList
     * @return
     */
    private String findPrimaryFileList(HtScanningMetadataDTO mainFileDTO, String level, HtPathConfigurationDTO pathDTO,
                                       List<String> bandsList, boolean preProcess) {
        List<HtScanningMetadataDTO> resultList = null;
        //标准化处理
        if (preProcess) {
            resultList = new ArrayList<>();
            resultList.add(mainFileDTO);
        } else {
            String mainFileDate = mainFileDTO.getFileDate();
            String leftTime = TIME_FORMAT.format(LocalDateTime.parse(mainFileDate, TIME_FORMAT).plusMinutes(-1 * DIFF_TIME_MINUTE));
            String rightTime = TIME_FORMAT.format(LocalDateTime.parse(mainFileDate, TIME_FORMAT).plusMinutes(DIFF_TIME_MINUTE));
            List<HtScanningMetadataDTO> dataList = htScanningMetadataService
                    .listByBandsAndDataLevelRangeTime(bandsList, level, leftTime, rightTime);
            if (dataList.isEmpty()) {
                return null;
            }
            double lon = Double.parseDouble(mainFileDTO.getLon().substring(1));
            double lat = Double.parseDouble(mainFileDTO.getLat().substring(1));
            resultList = dataList.stream().filter(dto -> {
                double dLon = Double.parseDouble(dto.getLon().substring(1));
                double dLat = Double.parseDouble(dto.getLat().substring(1));
                return dLon >= lon - 0.5 && dLon <= lon + 0.5 && dLat >= lat - 0.5 && dLat <= lat + 0.5;
            }).collect(Collectors.toList());
            //校验查询出的数据是否和输入通道数量匹配
            Map<String, List<HtScanningMetadataDTO>> hash = resultList.stream().collect(Collectors.groupingBy(HtScanningMetadataDTO::getBands));
            if (hash.size() != bandsList.size()) {
                logger.info("查询出的通道数量与输入数量不匹配");
                return null;
            }
            List<HtScanningMetadataDTO> tempList = new ArrayList<>();
            long mainFileMilli = LocalDateTime.parse(mainFileDate, TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            hash.values().forEach(list -> {
                HtScanningMetadataDTO resultDTO = list.stream().min((a, b) -> {
                    long aMilli = LocalDateTime.parse(a.getFileDate(), TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    long bMilli = LocalDateTime.parse(b.getFileDate(), TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    return Math.abs(aMilli - mainFileMilli) > Math.abs(bMilli - mainFileMilli) ? 1 : -1;
                }).get();
                tempList.add(resultDTO);
            });
            resultList = tempList;
        }
        //判断磁盘上是否存在文件
        StringBuilder sb = new StringBuilder();
        for (HtScanningMetadataDTO dto : resultList) {
            String currentFilePath = pathDTO.getClientPath() + File.separator + dto.getFilePath();
            File file = new File(currentFilePath);
            if (!file.exists()) {
                return null;
            }
            sb.append(",").append(currentFilePath);
        }
        return sb.substring(1);
    }

    private Map<String, String> geoRule(String geoRule, boolean preProcess, HtScanningMetadataDTO mainFileDTO, String geoPathPrefix) {
        StringBuilder geoPathSb = new StringBuilder();
        StringBuilder rpbPathSb = new StringBuilder();
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("geo", "");
        resultMap.put("rpb", "");
        if (StringUtils.isBlank(geoRule)) {
            return resultMap;
        }
        RuleVO geoRuleVO = JSON.parseObject(geoRule, RuleVO.class);
        if (NULL.equals(geoRuleVO.getMatchRule()) || NULL.equals(geoRuleVO.getLevel())) {
            return resultMap;
        }
        Map<String, String> timeRangeParam = parseTimeRange(mainFileDTO.getFileDate(), geoRuleVO.getTimeRange());
        String[] mathexpArr = geoRuleVO.getMatchRule().split("\\|");
        List<HtScanningMetadataDTO> ruleFileList = null;
        //标准化处理根据传感器，时间范围，数据等级，查询
        String startTime = timeRangeParam.get("startTime");
        String endTime = timeRangeParam.get("endTime");
        if (preProcess) {
            ruleFileList = htScanningMetadataService.listBySensorAndRangeTimeAndLevelAndBand(mainFileDTO.getSensor(), startTime,
                    endTime, geoRuleVO.getLevel(), mainFileDTO.getBands());
            Set<String> mathexpHashSet = new HashSet<>(Arrays.asList(mathexpArr));
            ruleFileList = ruleFileList.stream().filter(item -> {
                String name = item.getFileName();
                String[] splitName = name.split("_");
                return mathexpHashSet.contains(splitName[splitName.length - 2]);
            }).collect(Collectors.toList());
            if (ruleFileList.size() != mathexpHashSet.size()) {
                logger.info("主文件{}扫描过程中，查询geo文件和rpb文件时输入参数和查询出的数量不一致", mainFileDTO.getFileName());
                return null;
            }
        } else {
            //TODO 专业处理时HTHTGEO扫描规则
//            ruleFileList = htScanningMetadataService.listByProductIidAndBandsAndDataLevelAndLonAndLatAndTimeRange(mathexpArr[0],mainFileDTO.getBands(),
//                    geoRuleVO.getLevel(),mainFileDTO.getLon(),mainFileDTO.getLat(),startTime,endTime);
        }
        if (ruleFileList.isEmpty()) {
            logger.info("未查询到任何geo或rgb文件");
            return null;
        }
        for (HtScanningMetadataDTO fileDTO : ruleFileList) {
            String currentFilePath = geoPathPrefix + File.separator + fileDTO.getFilePath();
            File file = new File(currentFilePath);
            if (!file.exists()) {
                logger.info("{}在磁盘上不存在", fileDTO.getFileName());
                return null;
            }
            if ("HDF".equals(fileDTO.getFileType())) {
                geoPathSb.append(",").append(currentFilePath);
            } else if ("RPB".equals(fileDTO.getFileType())) {
                rpbPathSb.append(",").append(currentFilePath);
            }
        }
        String geoPath = geoPathSb.toString();
        String rpbPath = rpbPathSb.toString();
        if (StringUtils.isNotBlank(geoPath)) {
            resultMap.put("geo", geoPath.substring(1));
        }
        if (StringUtils.isNotBlank(rpbPath)) {
            resultMap.put("rpb", rpbPath.substring(1));
        }
        return resultMap;
    }

    private String l2Rule(String rule, HtScanningMetadataDTO mainFileDTO, String l2PathPrefix) {
        StringBuilder filePathSb = new StringBuilder();
        if (StringUtils.isBlank(rule)) {
            return filePathSb.toString();
        }
        RuleVO l2RuleVO = JSON.parseObject(rule, RuleVO.class);
        if (NULL.equals(l2RuleVO.getMatchRule()) || NULL.equals(l2RuleVO.getLevel())) {
            return filePathSb.toString();
        }
        Map<String, String> timeRangeParam = parseTimeRange(mainFileDTO.getFileDate(), l2RuleVO.getTimeRange());
        String[] mathexpArr = l2RuleVO.getMatchRule().split("\\|");
        List<HtScanningMetadataDTO> ruleFileList;
        String startTime = timeRangeParam.get("startTime");
        String endTime = timeRangeParam.get("endTime");
        boolean isSingleTimes = false;
        if (startTime.equals(endTime)) {
            isSingleTimes = true;
            startTime = TIME_FORMAT.format(LocalDateTime.parse(mainFileDTO.getFileDate(), TIME_FORMAT).plusMinutes(-1 * DIFF_TIME_MINUTE));
            endTime = TIME_FORMAT.format(LocalDateTime.parse(mainFileDTO.getFileDate(), TIME_FORMAT).plusMinutes(DIFF_TIME_MINUTE));
            List<HtScanningMetadataDTO> dataList = htScanningMetadataService
                    .listByProductIidListAndTimeRangeAndLevel(Arrays.asList(mathexpArr), startTime, endTime, l2RuleVO.getLevel());
            double lon = Double.parseDouble(mainFileDTO.getLon().substring(1));
            double lat = Double.parseDouble(mainFileDTO.getLat().substring(1));
            ruleFileList = dataList.stream().filter(dto -> {
                double dLon = Double.parseDouble(dto.getLon().substring(1));
                double dLat = Double.parseDouble(dto.getLat().substring(1));
                return dLon >= lon - 0.5 && dLon <= lon + 0.5 && dLat >= lat - 0.5 && dLat <= lat + 0.5;
            }).collect(Collectors.toList());
        } else {
            //TODO L2多时次文件查询规则，暂时用不到
            logger.info("多时次查询");
            ruleFileList = new ArrayList<>();
        }
        Map<String, List<HtScanningMetadataDTO>> filterMap = ruleFileList.stream()
                .collect(Collectors.groupingBy(HtScanningMetadataDTO::getProductIid));
        if (filterMap.size() != mathexpArr.length) {
            logger.info("与主文件{}要求的L2级文件数量与L2级规则产品标识数量不一致", mainFileDTO.getFileName());
            return null;
        }
        if (isSingleTimes) {
            List<HtScanningMetadataDTO> tempList = new ArrayList<>();
            long mainFileMilli = LocalDateTime.parse(mainFileDTO.getFileDate(), TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            filterMap.values().forEach(list -> {
                HtScanningMetadataDTO resultDTO = list.stream().min((a, b) -> {
                    long aMilli = LocalDateTime.parse(a.getFileDate(), TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    long bMilli = LocalDateTime.parse(b.getFileDate(), TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    return Math.abs(aMilli - mainFileMilli) > Math.abs(bMilli - mainFileMilli) ? 1 : -1;
                }).get();
                tempList.add(resultDTO);
            });
            ruleFileList = tempList;
        }
        for (HtScanningMetadataDTO fileDTO : ruleFileList) {
            String currentPath = l2PathPrefix + File.separator + fileDTO.getFilePath();
            File file = new File(currentPath);
            if (!file.exists()) {
                return null;
            }
            filePathSb.append(",").append(currentPath);
        }
        return filePathSb.substring(1);
    }

    private String nwpRule(String nwpFile, String mainFileDate, String npwPathPrefix) {
        StringBuilder filePathSb = new StringBuilder();
        if (StringUtils.isBlank(nwpFile)) {
            return filePathSb.toString();
        }
        RuleVO nwpRuleVO = JSON.parseObject(nwpFile, RuleVO.class);
        if (NULL.equals(nwpRuleVO.getMatchRule()) || NULL.equals(nwpRuleVO.getLevel())) {
            return filePathSb.toString();
        }
        //将主文件时间转为世界时来匹配数值预报数据
        mainFileDate = TIME_FORMAT.format(LocalDateTime.parse(mainFileDate, TIME_FORMAT).plusHours(-8L));
        //查询48小时内在数据库种条件要素的文件是否存在，并且数量至少为3个。
        Long earlyHour = (-1) * 48L;
        String earlyTime = TIME_FORMAT.format(LocalDateTime.parse(mainFileDate, TIME_FORMAT).plusHours(earlyHour));
        String[] mathexpArr = nwpRuleVO.getMatchRule().split("\\|");
        List<DbDmsScanningMetadataAuxInfoDTO> earlyAllData = dbDmsScanningMetadataAuxInfoService.
                listByFileWantListAndRangeTimeOrder(Arrays.asList(mathexpArr), earlyTime, mainFileDate);
        Map<String, Long> countAllNwpMap = earlyAllData.stream()
                .collect(Collectors.groupingBy(DbDmsScanningMetadataAuxInfoDTO::getFileWant, Collectors.counting()));
        if (mathexpArr.length != countAllNwpMap.size()) {
            logger.info("48小时内满足的要素种类数量不正确");
            return null;
        }
        for (Map.Entry<String, Long> entry : countAllNwpMap.entrySet()) {
            if (entry.getValue() < 3) {
                logger.info("{}--->要素的数量在48小时的范围内小于3", entry.getKey());
            }
        }
        //过滤筛筛选出符合规则要求的时次的要素文件  每种要素3个数据信息
        Map<String, String> timeRangeParam = parseTimeRange(mainFileDate, nwpRuleVO.getTimeRange());
        LocalDateTime nwpStartTime = LocalDateTime.parse(timeRangeParam.get("startTime"), TIME_FORMAT);
        LocalDateTime nwpEndTime = LocalDateTime.parse(timeRangeParam.get("endTime"), TIME_FORMAT);
        //key:要素 value:[nwp数据（时间+时次会重复）]
        Map<String, List<DbDmsScanningMetadataAuxInfoDTO>> filterMap = earlyAllData.stream().filter(dto -> {
            LocalDateTime dtoLocalDateTime = LocalDateTime.parse(dto.getFileDate(), TIME_FORMAT).plusHours(dto.getFileTimeSecond());
            return !nwpStartTime.isAfter(dtoLocalDateTime) && !nwpEndTime.isBefore(dtoLocalDateTime);
        }).collect(Collectors.groupingBy(DbDmsScanningMetadataAuxInfoDTO::getFileWant));
        for (Map.Entry<String, List<DbDmsScanningMetadataAuxInfoDTO>> entry : filterMap.entrySet()) {
            List<DbDmsScanningMetadataAuxInfoDTO> wantList = entry.getValue();
            List<DbDmsScanningMetadataAuxInfoDTO> nwpInfoList = new ArrayList<>();
            Map<String, DbDmsScanningMetadataAuxInfoDTO> hash = new HashMap<>();
            for (DbDmsScanningMetadataAuxInfoDTO nwpDTO : wantList) {
                String formatDate = TIME_FORMAT.format(LocalDateTime.parse(nwpDTO.getFileDate(), TIME_FORMAT).plusHours(nwpDTO.getFileTimeSecond()));
                if (!hash.containsKey(formatDate)) {
                    hash.put(formatDate, nwpDTO);
                    nwpInfoList.add(nwpDTO);
                }
            }
            if (nwpInfoList.size() < 3) {
                logger.info("{}要素在规则的时间范围内个数小于3", entry.getKey());
                return null;
            }
            //查找最接近的三个数量文件
            List<Long> nearPrimaryFileTime = findNearDateNwpFile(nwpInfoList, mainFileDate);
            //判断磁盘上是否存在
            StringBuilder sb = new StringBuilder();
            for (Long keyMilli : nearPrimaryFileTime) {
                String keyTime = TIME_FORMAT.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(keyMilli), ZoneId.systemDefault()));
                DbDmsScanningMetadataAuxInfoDTO currentFileDTO = hash.get(keyTime);
                String currentPath = npwPathPrefix + File.separator + currentFileDTO.getFilePath();
                File file = new File(currentPath);
                if (!file.exists()) {
                    logger.info("{}--->nwp文件在磁盘上不存在", file.getName());
                    return null;
                }
                sb.append(",").append(currentPath);
            }
            filePathSb.append(";").append(sb.substring(1));
        }
        return filePathSb.substring(1);
    }

    private List<Long> findNearDateNwpFile(List<DbDmsScanningMetadataAuxInfoDTO> nwpInfoList, String mainFileDate) {
        long primaryFileMilli = LocalDateTime.parse(mainFileDate, TIME_FORMAT).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        List<Long> nearPrimaryFileTime = new ArrayList<>();
        for (int j = 0; j < nwpInfoList.size(); j++) {
            DbDmsScanningMetadataAuxInfoDTO metadataAuxInfoDTO = nwpInfoList.get(j);
            long currentFileMilli = LocalDateTime.parse(metadataAuxInfoDTO.getFileDate(), TIME_FORMAT)
                    .plusHours(metadataAuxInfoDTO.getFileTimeSecond())
                    .atZone(ZoneId.systemDefault())
                    .toInstant()
                    .toEpochMilli();
            if (j < 3) {
                nearPrimaryFileTime.add(currentFileMilli);
            } else {
                //主文件时次右边是从大到小遍历，所以直接替换，保持最右是最小最近的文件时间，最左边是最大的。
                if (currentFileMilli - primaryFileMilli >= 0) {
                    nearPrimaryFileTime.remove(0);
                    nearPrimaryFileTime.add(currentFileMilli);
                } else {
                    //左边进行比较
                    long dif = primaryFileMilli - currentFileMilli;
                    long max = nearPrimaryFileTime.stream().max((a, b) -> Math.abs(a - primaryFileMilli) > Math.abs(b - primaryFileMilli) ? 1 : -1).get();
                    if (dif < Math.abs(max - primaryFileMilli)) {
                        nearPrimaryFileTime.remove(max);
                        nearPrimaryFileTime.add(currentFileMilli);
                    } else {
                        break;
                    }
                }
            }
        }
        return nearPrimaryFileTime;
    }


    private Map<String, String> parseTimeRange(String mainFileDate, String timeRange) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("startTime", mainFileDate);
        resultMap.put("endTime", mainFileDate);
        if (timeRange.equals("NULL")) {
            return resultMap;
        }
        String[] timeSplit = timeRange.split(",");
        long startPlusMinute = -1 * Math.abs(Long.parseLong(timeSplit[0]));
        long endPlusMinute = Math.abs(Long.parseLong(timeSplit[1]));
        LocalDateTime mainFileLocalDateTime = LocalDateTime.parse(mainFileDate, TIME_FORMAT);
        String startTime = TIME_FORMAT.format(mainFileLocalDateTime.plusMinutes(startPlusMinute));
        String endTime = TIME_FORMAT.format(mainFileLocalDateTime.plusMinutes(endPlusMinute));
        resultMap.put("startTime", startTime);
        resultMap.put("endTime", endTime);
        return resultMap;
    }


}
